package site.cyningxu.deviceinfochanger;

import android.app.Activity;
import android.app.Application;
import android.content.ContentResolver;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;

import com.google.gson.Gson;

import java.util.ArrayList;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

/**
 * Created by xuxingcan on 17-8-9.
 */

public class XposedInit {
    private Gson gson = new Gson();
    private DeviceInfoModel deviceInfoModel;
    private static ArrayList<String> hookPkgNameList = new ArrayList<>();

    static {
        hookPkgNameList.add("com.jifen.qukan");
        hookPkgNameList.add("com.coohua.xinwenzhuan");
    }

    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        if (!hookPkgNameList.contains(loadPackageParam.packageName)) {
            return;
        }
        try {
            XSharedPreferences xSharedPreferences = new XSharedPreferences(BuildConfig.APPLICATION_ID, "device_info");
            deviceInfoModel = gson.fromJson(xSharedPreferences.getString("device_info_key", ""), DeviceInfoModel.class);
        } catch (Exception e) {
            XposedBridge.log(e);
        }
        hookDeviceField(loadPackageParam.classLoader);
        hookDeviceInfo(loadPackageParam.classLoader);
    }

    private void hookDeviceField(ClassLoader loader) {
        findAndHookMethod(Application.class, "onCreate", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                fixDeviceFieldValue(Build.class, "RADIO");
                fixDeviceFieldValue(Build.VERSION.class, "RELEASE");
                fixDeviceFieldValue(Build.class, "SERIAL");
                fixDeviceFieldValue(Build.class, "MODEL");
                fixDeviceFieldValue(Build.class, "BOARD");
                fixDeviceFieldValue(Build.class, "BOOTLOADER");
                fixDeviceFieldValue(Build.class, "BRAND");
                fixDeviceFieldValue(Build.class, "DEVICE");
                fixDeviceFieldValue(Build.class, "HARDWARE");
                fixDeviceFieldValue(Build.class, "PRODUCT");
            }
        });
        findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                fixDeviceFieldValue(Build.class, "RADIO");
                fixDeviceFieldValue(Build.VERSION.class, "RELEASE");
                fixDeviceFieldValue(Build.class, "SERIAL");
                fixDeviceFieldValue(Build.class, "MODEL");
                fixDeviceFieldValue(Build.class, "BOARD");
                fixDeviceFieldValue(Build.class, "BOOTLOADER");
                fixDeviceFieldValue(Build.class, "BRAND");
                fixDeviceFieldValue(Build.class, "DEVICE");
                fixDeviceFieldValue(Build.class, "HARDWARE");
                fixDeviceFieldValue(Build.class, "PRODUCT");
            }
        });
    }

    private void hookDeviceInfo(final ClassLoader loader) {
        findAndHookMethod("android.provider.Settings.Secure", loader, "getString", ContentResolver.class, String.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                if (!"android_id".equals(param.args[1])) {
                    return;
                }
                fixDeviceInfoResult(param, "android_id");
            }
        });
        findAndHookMethod("android.net.wifi.WifiInfo", loader, "getMacAddress", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "MacAddress");
            }
        });
        findAndHookMethod("android.bluetooth.BluetoothAdapter", loader, "getAddress", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "BluetoothAddress");
            }
        });
        findAndHookMethod("android.net.wifi.WifiInfo", loader, "getSSID", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "SSID");
            }
        });
        findAndHookMethod("android.net.wifi.WifiInfo", loader, "getBSSID", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "BSSID");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getDeviceId", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "DeviceId");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getLine1Number", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "Line1Number");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getSimSerialNumber", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "SimSerialNumber");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getSubscriberId", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "SubscriberId");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getSimCountryIso", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "SimCountryIso");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getSimOperator", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "SimOperator");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getSimOperatorName", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "SimOperatorName");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getNetworkCountryIso", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "NetworkCountryIso");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getNetworkOperator", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "NetworkOperator");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getNetworkOperatorName", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                fixDeviceInfoResult(param, "NetworkOperatorName");
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getNetworkType", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                if (deviceInfoModel != null && deviceInfoModel.NetworkType != Integer.MAX_VALUE) {
                    param.setResult(deviceInfoModel.NetworkType);
                }
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getPhoneType", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                if (deviceInfoModel != null && deviceInfoModel.PhoneType != Integer.MAX_VALUE) {
                    param.setResult(deviceInfoModel.PhoneType);
                }
            }
        });
        findAndHookMethod("android.telephony.TelephonyManager", loader, "getSimState", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                if (deviceInfoModel != null && deviceInfoModel.SimState != Integer.MAX_VALUE) {
                    param.setResult(deviceInfoModel.SimState);
                }
            }
        });
    }

    private void fixDeviceInfoResult(XC_MethodHook.MethodHookParam param, String key) {
        if (deviceInfoModel == null || deviceInfoModel.DeviceInfoHashMap == null
                || !deviceInfoModel.DeviceInfoHashMap.containsKey(key) || TextUtils.isEmpty(deviceInfoModel.DeviceInfoHashMap.get(key))) {
            return;
        }
        XposedBridge.log("fixDeviceInfoResult " + key + " to " + deviceInfoModel.DeviceInfoHashMap.get(key));
        param.setResult(deviceInfoModel.DeviceInfoHashMap.get(key));
    }

    private void fixDeviceFieldValue(Class cls, String key) {
        if (deviceInfoModel == null || deviceInfoModel.DeviceInfoHashMap == null
                || !deviceInfoModel.DeviceInfoHashMap.containsKey(key) || TextUtils.isEmpty(deviceInfoModel.DeviceInfoHashMap.get(key))) {
            return;
        }
        XposedBridge.log("fixDeviceFieldValue " + key + " to " + deviceInfoModel.DeviceInfoHashMap.get(key));
        XposedHelpers.setStaticObjectField(cls, key, deviceInfoModel.DeviceInfoHashMap.get(key));
    }
}
